/*
 * PROJECT:     ReactOS API tests
 * LICENSE:     MIT (https://spdx.org/licenses/MIT)
 * PURPOSE:     Tests for cos
 * COPYRIGHT:   Copyright 2021 Timo Kreuzer <timo.kreuzer@reactos.org>
 */

#if !defined(_CRTBLD) && !defined(_M_IX86)
#define _CRTBLD // we don't want inline cos!
#endif
#include "math_helpers.h"

#ifdef _MSC_VER
#pragma function(cos)
#endif

#if !defined(_M_IX86)
#define HAS_COSF
#elif (defined(TEST_UCRTBASE) || defined(TEST_STATIC_CRT))
#define HAS_LIBM_SSE2
#endif


// These are expected to match exactly
static TESTENTRY_DBL s_cos_exact_tests[] =
{
    { 0x0000000000000000 /*  0.000000 */, 0x3ff0000000000000 /*  1.000000 */ },
    { 0x8000000000000000 /* -0.000000 */, 0x3ff0000000000000 /*  1.000000 */ },
    { 0x7ff0000000000000 /*  1.#INF00 */, 0xfff8000000000000 /* -1.#IND00 */ },
    { 0x7ff0000000000001 /*  1.#SNAN0 */, 0x7ff8000000000001 /*  1.#QNAN0 */ },
    { 0x7ff7ffffffffffff /*  1.#SNAN0 */, 0x7fffffffffffffff /*  1.#QNAN0 */ },
    { 0x7ff8000000000000 /*  1.#QNAN0 */, 0x7ff8000000000000 /*  1.#QNAN0 */ },
    { 0x7ff8000000000001 /*  1.#QNAN0 */, 0x7ff8000000000001 /*  1.#QNAN0 */ },
    { 0x7fffffffffffffff /*  1.#QNAN0 */, 0x7fffffffffffffff /*  1.#QNAN0 */ },
    { 0xfff0000000000000 /* -1.#INF00 */, 0xfff8000000000000 /* -1.#IND00 */ },
    { 0xfff0000000000001 /* -1.#SNAN0 */, 0xfff8000000000001 /* -1.#QNAN0 */ },
    { 0xfff7ffffffffffff /* -1.#SNAN0 */, 0xffffffffffffffff /* -1.#QNAN0 */ },
    { 0xfff8000000000000 /* -1.#IND00 */, 0xfff8000000000000 /* -1.#IND00 */ },
    { 0xfff8000000000001 /* -1.#QNAN0 */, 0xfff8000000000001 /* -1.#QNAN0 */ },
    { 0xffffffffffffffff /* -1.#QNAN0 */, 0xffffffffffffffff /* -1.#QNAN0 */ },
};

void Test_cos_exact(void)
{
    for (int i = 0; i < _countof(s_cos_exact_tests); i++)
    {
        double x = u64_to_dbl(s_cos_exact_tests[i].x);
        double z = cos(x);
        ok_eq_dbl_exact("cos", s_cos_exact_tests[i].x, z, s_cos_exact_tests[i].result);
    }
}

// This table is autogenerated by `python gen_math_tests.py cos`
static TESTENTRY_DBL_APPROX s_cos_approx_tests[] =
{
//  {    x,                     {    y_rounded,               y_difference           } }
    {   -0x1.eadfb4c5d390cp+14, {     0x1.0000000000000p+0,   -0x1.2403af5395118p-83 }, 1 }, // cos(-31415.926535897932) == 1.0
    {   -0x1.ec19dd8b70e4fp+14, {    -0x1.0000000000000p+0,    0x1.0f1be751b19afp-80 }, 1 }, // cos(-31494.466352237676) == -1.0
    {   -0x1.ed5406510e393p+14, {     0x1.0000000000000p+0,   -0x1.514ccf9309168p-80 }, 1 }, // cos(-31573.006168577424) == 1.0
    {   -0x1.ee8e2f16ab8d6p+14, {    -0x1.0000000000000p+0,    0x1.f89acdeca16f8p-83 }, 1 }, // cos(-31651.545984917168) == -1.0
    {   -0x1.efc857dc48e19p+14, {     0x1.0000000000000p+0,   -0x1.8a2a8130946edp-86 }, 1 }, // cos(-31730.08580125691) == 1.0
    {   -0x1.f10280a1e635cp+14, {    -0x1.0000000000000p+0,    0x1.4d1980d12703cp-81 }, 1 }, // cos(-31808.625617596656) == -1.0
    {   -0x1.f23ca967838a0p+14, {     0x1.0000000000000p+0,   -0x1.e0bc7fce954cep-80 }, 1 }, // cos(-31887.165433936403) == 1.0
    {   -0x1.f376d22d20de3p+14, {    -0x1.0000000000000p+0,    0x1.08975deb65378p-81 }, 1 }, // cos(-31965.705250276147) == -1.0
    {   -0x1.f4b0faf2be326p+14, {     0x1.0000000000000p+0,   -0x1.29a43972f6ee3p-88 }, 1 }, // cos(-32044.24506661589) == 1.0
    {    -0x1.3a28c59d5433bp+8, {     0x1.0000000000000p+0,   -0x1.391074c78e91bp-99 }, 1 }, // cos(-314.1592653589793) == 1.0
    {    -0x1.395fb5c2b2124p+8, {     0x1.6a09e667f3b35p-1,    0x1.c6ea9224d6e68p-56 }, 1 }, // cos(-313.37386719558185) == 0.70710678118653072201
#ifndef _M_IX86 // Too inaccurate on x86
    {    -0x1.3896a5e80ff0ep+8, {    0x1.08fb09c9658edp-47,   0x1.39665a47cd268p-101 }, 1 }, // cos(-312.58846903218443) == 7.3546895818865909981e-15
#endif
    {    -0x1.37cd960d6dcf7p+8, {    -0x1.6a09e667f3c42p-1,    0x1.b22c3e6464596p-55 }, 1 }, // cos(-311.80307086878696) == -0.70710678118656051527
    {    -0x1.37048632cbae1p+8, {    -0x1.0000000000000p+0,    0x1.9bd23bd28b82dp-94 }, 1 }, // cos(-311.01767270538954) == -1.0
    {    -0x1.363b7658298cap+8, {    -0x1.6a09e667f3b7ap-1,    0x1.d9a744d93e3bep-57 }, 1 }, // cos(-310.23227454199207) == -0.70710678118653834505
#ifndef _M_IX86 // Too inaccurate on x86
    {    -0x1.3572667d876b4p+8, {   -0x1.46b249ab1552fp-46,   0x1.2c459e91da244p-100 }, 1 }, // cos(-309.44687637859465) == -1.8135295298202829104e-14
#endif
    {    -0x1.34a956a2e549dp+8, {     0x1.6a09e667f3bfdp-1,   -0x1.6134906e7f7d6p-57 }, 1 }, // cos(-308.6614782151972) == 0.70710678118655289223
    {    -0x1.33e046c843287p+8, {     0x1.0000000000000p+0,   -0x1.5ecb021cebd82p-92 }, 1 }, // cos(-307.87608005179976) == 1.0
    {    -0x1.921fb54442d18p+1, {    -0x1.0000000000000p+0,   0x1.377ce858a5d48p-107 }, 1 }, // cos(-3.141592653589793) == -1.0
    {    -0x1.83c325a66500ep+1, {    -0x1.fcc7d8c64135fp-1,    0x1.367f102ff54fcp-55 }, 1 }, // cos(-3.029392915961586) == -0.99371220989324257031
    {    -0x1.7566960887304p+1, {    -0x1.f329c0558e969p-1,    0x1.5b0f4d2ea81b7p-55 }, 1 }, // cos(-2.9171931783333793) == -0.97492791218182358171
    {    -0x1.670a066aa95fap+1, {    -0x1.e344ad05d3f86p-1,    0x1.b45a113d06283p-55 }, 1 }, // cos(-2.8049934407051724) == -0.94388333030836752678
    {    -0x1.58ad76cccb8f0p+1, {    -0x1.cd4bca9cb5c70p-1,   -0x1.a5a57d0c3fb12p-55 }, 1 }, // cos(-2.6927937030769655) == -0.90096886790241908069
    {    -0x1.4a50e72eedbe6p+1, {    -0x1.b185d590d5a44p-1,    0x1.65c52a9fecdc7p-57 }, 1 }, // cos(-2.5805939654487586) == -0.84672419922828411483
    {    -0x1.3bf457910fedcp+1, {    -0x1.904c37505de4bp-1,    0x1.fa8a75ecd3b6bp-55 }, 1 }, // cos(-2.4683942278205517) == -0.78183148246802974871
    {    -0x1.2d97c7f3321d2p+1, {    -0x1.6a09e667f3bccp-1,    0x1.4da530b7ba971p-59 }, 1 }, // cos(-2.356194490192345) == -0.70710678118654745945
    {    -0x1.1f3b3855544c8p+1, {    -0x1.3f3a0e28bedd1p-1,    0x1.879da02139bebp-56 }, 1 }, // cos(-2.243994752564138) == -0.62348980185873346213
    {    -0x1.10dea8b7767bep+1, {    -0x1.106682221cd89p-1,   -0x1.2c8a7a33558b4p-55 }, 1 }, // cos(-2.131795014935931) == -0.53203207651533649319
    {    -0x1.0282191998ab4p+1, {    -0x1.bc4c04d71abc0p-2,    0x1.1b13a1dc0682cp-56 }, 1 }, // cos(-2.019595277307724) == -0.43388373911755804954
    {    -0x1.e84b12f775b54p+0, {    -0x1.5234aca69a9fcp-2,   -0x1.d8fbf671ab766p-56 }, 1 }, // cos(-1.9073955396795172) == -0.33027906195516701159
    {    -0x1.cb91f3bbba140p+0, {    -0x1.c7b90e3024580p-3,    0x1.802c738d03774p-60 }, 1 }, // cos(-1.7951958020513104) == -0.22252093395631433606
    {    -0x1.aed8d47ffe72cp+0, {    -0x1.ca9b4332d6f5dp-4,    0x1.f703667590af9p-58 }, 1 }, // cos(-1.6829960644231035) == -0.11196447610330779328
#ifndef _M_IX86 // Too inaccurate on x86
    {    -0x1.921fb54442d18p+0, {    0x1.1a62633145c07p-54,  -0x1.f1976b7ed8fbcp-110 }, 1 }, // cos(-1.5707963267948966) == 6.1232339957367658861e-17
#endif
    {    -0x1.7566960887304p+0, {     0x1.ca9b4332d6f65p-4,    0x1.1c778073c64dcp-58 }, 1 }, // cos(-1.4585965891666897) == 0.11196447610330791497
    {    -0x1.58ad76cccb8f0p+0, {     0x1.c7b90e3024584p-3,    0x1.04d91ba721277p-57 }, 1 }, // cos(-1.3463968515384828) == 0.22252093395631445546
    {    -0x1.3bf457910fedcp+0, {     0x1.5234aca69a9ffp-2,   -0x1.d2b681016ab21p-56 }, 1 }, // cos(-1.2341971139102759) == 0.33027906195516712719
    {    -0x1.1f3b3855544c8p+0, {     0x1.bc4c04d71abc2p-2,   -0x1.27b88616e3651p-56 }, 1 }, // cos(-1.121997376282069) == 0.43388373911755815988
    {    -0x1.0282191998ab4p+0, {     0x1.106682221cd8ap-1,    0x1.d1e51e44a788ep-56 }, 1 }, // cos(-1.009797638653862) == 0.53203207651533659689
    {    -0x1.cb91f3bbba140p-1, {     0x1.3f3a0e28bedd2p-1,   -0x1.50b335a42a636p-55 }, 1 }, // cos(-0.8975979010256552) == 0.62348980185873355788
    {    -0x1.921fb54442d18p-1, {     0x1.6a09e667f3bcdp-1,   -0x1.ec4c7696139d5p-56 }, 1 }, // cos(-0.7853981633974483) == 0.70710678118654754605
    {    -0x1.58ad76cccb8f0p-1, {     0x1.904c37505de4bp-1,    0x1.8b6da23322397p-56 }, 1 }, // cos(-0.6731984257692414) == 0.78183148246802982507
    {    -0x1.1f3b3855544c8p-1, {     0x1.b185d590d5a44p-1,    0x1.ff81ed9ef2c94p-55 }, 1 }, // cos(-0.5609986881410345) == 0.84672419922828417999
    {    -0x1.cb91f3bbba140p-2, {     0x1.cd4bca9cb5c71p-1,   -0x1.c110117824236p-57 }, 1 }, // cos(-0.4487989505128276) == 0.90096886790241913383
    {    -0x1.58ad76cccb8f0p-2, {     0x1.e344ad05d3f86p-1,   -0x1.fa507ebf8867ap-58 }, 1 }, // cos(-0.3365992128846207) == 0.94388333030836756723
    {    -0x1.cb91f3bbba140p-3, {     0x1.f329c0558e969p-1,   -0x1.7edb36a12557fp-57 }, 1 }, // cos(-0.2243994752564138) == 0.97492791218182360896
    {    -0x1.cb91f3bbba140p-4, {     0x1.fcc7d8c64135fp-1,   -0x1.700e7bcbe5aa3p-56 }, 1 }, // cos(-0.1121997376282069) == 0.99371220989324258402
    {                 0x0.0p+0, {     0x1.0000000000000p+0,                 0x0.0p+0 }, 1 }, // cos(0.0) == 1.0
    {     0x1.cb91f3bbba140p-4, {     0x1.fcc7d8c64135fp-1,   -0x1.700e7bcbe5aa3p-56 }, 1 }, // cos(0.1121997376282069) == 0.99371220989324258402
    {     0x1.cb91f3bbba140p-3, {     0x1.f329c0558e969p-1,   -0x1.7edb36a12557fp-57 }, 1 }, // cos(0.2243994752564138) == 0.97492791218182360896
    {     0x1.58ad76cccb8f0p-2, {     0x1.e344ad05d3f86p-1,   -0x1.fa507ebf8867ap-58 }, 1 }, // cos(0.3365992128846207) == 0.94388333030836756723
    {     0x1.cb91f3bbba140p-2, {     0x1.cd4bca9cb5c71p-1,   -0x1.c110117824236p-57 }, 1 }, // cos(0.4487989505128276) == 0.90096886790241913383
    {     0x1.1f3b3855544c8p-1, {     0x1.b185d590d5a44p-1,    0x1.ff81ed9ef2c94p-55 }, 1 }, // cos(0.5609986881410345) == 0.84672419922828417999
    {     0x1.58ad76cccb8f0p-1, {     0x1.904c37505de4bp-1,    0x1.8b6da23322397p-56 }, 1 }, // cos(0.6731984257692414) == 0.78183148246802982507
    {     0x1.921fb54442d18p-1, {     0x1.6a09e667f3bcdp-1,   -0x1.ec4c7696139d5p-56 }, 1 }, // cos(0.7853981633974483) == 0.70710678118654754605
    {     0x1.cb91f3bbba140p-1, {     0x1.3f3a0e28bedd2p-1,   -0x1.50b335a42a636p-55 }, 1 }, // cos(0.8975979010256552) == 0.62348980185873355788
    {     0x1.0282191998ab4p+0, {     0x1.106682221cd8ap-1,    0x1.d1e51e44a788ep-56 }, 1 }, // cos(1.009797638653862) == 0.53203207651533659689
    {     0x1.1f3b3855544c8p+0, {     0x1.bc4c04d71abc2p-2,   -0x1.27b88616e3651p-56 }, 1 }, // cos(1.121997376282069) == 0.43388373911755815988
    {     0x1.3bf457910fedcp+0, {     0x1.5234aca69a9ffp-2,   -0x1.d2b681016ab21p-56 }, 1 }, // cos(1.2341971139102759) == 0.33027906195516712719
    {     0x1.58ad76cccb8f0p+0, {     0x1.c7b90e3024584p-3,    0x1.04d91ba721277p-57 }, 1 }, // cos(1.3463968515384828) == 0.22252093395631445546
    {     0x1.7566960887304p+0, {     0x1.ca9b4332d6f65p-4,    0x1.1c778073c64dcp-58 }, 1 }, // cos(1.4585965891666897) == 0.11196447610330791497
#ifndef _M_IX86 // Too inaccurate on x86
    {     0x1.921fb54442d18p+0, {    0x1.1a62633145c07p-54,  -0x1.f1976b7ed8fbcp-110 }, 1 }, // cos(1.5707963267948966) == 6.1232339957367658861e-17
#endif
    {     0x1.aed8d47ffe72cp+0, {    -0x1.ca9b4332d6f5dp-4,    0x1.f703667590af9p-58 }, 1 }, // cos(1.6829960644231035) == -0.11196447610330779328
    {     0x1.cb91f3bbba140p+0, {    -0x1.c7b90e3024580p-3,    0x1.802c738d03774p-60 }, 1 }, // cos(1.7951958020513104) == -0.22252093395631433606
    {     0x1.e84b12f775b54p+0, {    -0x1.5234aca69a9fcp-2,   -0x1.d8fbf671ab766p-56 }, 1 }, // cos(1.9073955396795172) == -0.33027906195516701159
    {     0x1.0282191998ab4p+1, {    -0x1.bc4c04d71abc0p-2,    0x1.1b13a1dc0682cp-56 }, 1 }, // cos(2.019595277307724) == -0.43388373911755804954
    {     0x1.10dea8b7767bep+1, {    -0x1.106682221cd89p-1,   -0x1.2c8a7a33558b4p-55 }, 1 }, // cos(2.131795014935931) == -0.53203207651533649319
    {     0x1.1f3b3855544c8p+1, {    -0x1.3f3a0e28bedd1p-1,    0x1.879da02139bebp-56 }, 1 }, // cos(2.243994752564138) == -0.62348980185873346213
    {     0x1.2d97c7f3321d2p+1, {    -0x1.6a09e667f3bccp-1,    0x1.4da530b7ba971p-59 }, 1 }, // cos(2.356194490192345) == -0.70710678118654745945
    {     0x1.3bf457910fedcp+1, {    -0x1.904c37505de4bp-1,    0x1.fa8a75ecd3b6bp-55 }, 1 }, // cos(2.4683942278205517) == -0.78183148246802974871
    {     0x1.4a50e72eedbe6p+1, {    -0x1.b185d590d5a44p-1,    0x1.65c52a9fecdc7p-57 }, 1 }, // cos(2.5805939654487586) == -0.84672419922828411483
    {     0x1.58ad76cccb8f0p+1, {    -0x1.cd4bca9cb5c70p-1,   -0x1.a5a57d0c3fb12p-55 }, 1 }, // cos(2.6927937030769655) == -0.90096886790241908069
    {     0x1.670a066aa95fap+1, {    -0x1.e344ad05d3f86p-1,    0x1.b45a113d06283p-55 }, 1 }, // cos(2.8049934407051724) == -0.94388333030836752678
    {     0x1.7566960887304p+1, {    -0x1.f329c0558e969p-1,    0x1.5b0f4d2ea81b7p-55 }, 1 }, // cos(2.9171931783333793) == -0.97492791218182358171
    {     0x1.83c325a66500ep+1, {    -0x1.fcc7d8c64135fp-1,    0x1.367f102ff54fcp-55 }, 1 }, // cos(3.029392915961586) == -0.99371220989324257031
    {     0x1.921fb54442d18p+1, {    -0x1.0000000000000p+0,   0x1.377ce858a5d48p-107 }, 1 }, // cos(3.141592653589793) == -1.0
    {    0x1.88b2f704a940ap+12, {     0x1.0000000000000p+0,   -0x1.601c7d1da2ff3p-85 }, 1 }, // cos(6283.185307179587) == 1.0
    {    0x1.88bf88025362bp+12, {     0x1.6a09e667f3e6dp-1,   -0x1.985187d9d5455p-55 }, 1 }, // cos(6283.970705342984) == 0.70710678118662213545
#ifndef _M_IX86 // Too inaccurate on x86
    {    0x1.88cc18fffd84dp+12, {   -0x1.e62a44fbaecdcp-42,    0x1.e03cc1e86af38p-96 }, 1 }, // cos(6284.756103506382) == -4.3180136364206742394e-13
#endif
    {    0x1.88d8a9fda7a6ep+12, {    -0x1.6a09e667f3d48p-1,   -0x1.406771f3f4b4dp-55 }, 1 }, // cos(6285.541501669779) == -0.70710678118658968493
    {    0x1.88e53afb51c8fp+12, {    -0x1.0000000000000p+0,    0x1.e3bc4d931655bp-85 }, 1 }, // cos(6286.326899833176) == -1.0
    {    0x1.88f1cbf8fbeb1p+12, {    -0x1.6a09e667f3635p-1,   -0x1.01acf80c7daafp-58 }, 1 }, // cos(6287.112297996574) == -0.70710678118638859229
#ifndef _M_IX86 // Too inaccurate on x86
    {    0x1.88fe5cf6a60d2p+12, {   -0x1.4bf20ea16eb60p-43,   0x1.c52cad7b06dffp-100 }, 1 }, // cos(6287.897696159971) == -1.4741343068232896286e-13
#endif
    {    0x1.890aedf4502f4p+12, {     0x1.6a09e667f4580p-1,   -0x1.e07ed3c6dd437p-57 }, 1 }, // cos(6288.683094323369) == 0.70710678118682322809
    {    0x1.89177ef1fa515p+12, {     0x1.0000000000000p+0,   -0x1.8e55576e32da8p-93 }, 1 }, // cos(6289.468492486766) == 1.0
    {    0x1.7f7ec53a8d491p+22, {     0x1.0000000000000p+0,   -0x1.d67ba289b6955p-64 }, 1 }, // cos(6283185.307179586) == 0.9999999999999999999
    {    0x1.812c48f8ba623p+22, {     0x1.0000000000000p+0,   -0x1.40c9dccb0a88bp-64 }, 1 }, // cos(6310674.242898497) == 0.99999999999999999993
    {    0x1.82d9ccb6e77b5p+22, {     0x1.0000000000000p+0,   -0x1.8f57e0f27aa24p-65 }, 1 }, // cos(6338163.178617408) == 0.99999999999999999996
    {    0x1.8487507514947p+22, {     0x1.0000000000000p+0,   -0x1.ac8776513bbadp-66 }, 1 }, // cos(6365652.114336318) == 0.99999999999999999998
    {    0x1.8634d43341ad9p+22, {     0x1.0000000000000p+0,   -0x1.595d20e1fb0b4p-67 }, 1 }, // cos(6393141.050055229) == 0.99999999999999999999
    {    0x1.87e257f16ec6bp+22, {     0x1.0000000000000p+0,   -0x1.f2510443aa4e1p-70 }, 1 }, // cos(6420629.98577414) == 1.0
    {    0x1.898fdbaf9bdfdp+22, {     0x1.0000000000000p+0,   -0x1.f57ae5798c4c9p-73 }, 1 }, // cos(6448118.921493051) == 1.0
    {    0x1.8b3d5f6dc8f8fp+22, {     0x1.0000000000000p+0,   -0x1.6c0104149b832p-68 }, 1 }, // cos(6475607.857211961) == 1.0
    {    0x1.8ceae32bf6121p+22, {     0x1.0000000000000p+0,   -0x1.2464f1f2d5fd7p-66 }, 1 }, // cos(6503096.792930872) == 0.99999999999999999998
};

void Test_cos_approx(void)
{
    for (int i = 0; i < _countof(s_cos_approx_tests); i++)
    {
        double x = s_cos_approx_tests[i].x;
        double expected = s_cos_approx_tests[i].expected.rounded;
        double z = cos(x);
        int64_t error = abs(ulp_error_precise(&s_cos_approx_tests[i].expected, z));
        ok(error <= s_cos_approx_tests[i].max_error,
            "cos(%.17e) = %.17e, expected %.17e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_cos_approx_tests[i].max_error);
    }
}

#ifdef HAS_COSF

// These are expected to match exactly
static TESTENTRY_DBL s_cosf_exact_tests[] =
{
    { 0x00000000 /*  0.000000 */, 0x3f800000 /*  1.000000 */ },
    { 0x80000000 /* -0.000000 */, 0x3f800000 /*  1.000000 */ },
    { 0x7f800000 /*  1.#INF00 */, 0x3f800000 /*  1.000000 */ },
    { 0x7f800001 /*  1.#SNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0x7fBFffff /*  1.#SNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0x7fC00000 /*  1.#QNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0x7fC80001 /*  1.#QNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0x7fFfffff /*  1.#QNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0xff800000 /* -1.#INF00 */, 0x3f800000 /*  1.000000 */ },
    { 0xff800001 /* -1.#SNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0xffBfffff /* -1.#SNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0xffC00000 /* -1.#IND00 */, 0x3f800000 /*  1.000000 */ },
    { 0xfff80001 /* -1.#QNAN0 */, 0x3f800000 /*  1.000000 */ },
    { 0xffffffff /* -1.#QNAN0 */, 0x3f800000 /*  1.000000 */ },
};

void Test_cosf_exact(void)
{
    for (int i = 0; i < _countof(s_cosf_exact_tests); i++)
    {
        float x = u64_to_dbl(s_cosf_exact_tests[i].x);
        float z = cosf(x);
        ok_eq_flt_exact("cosf", s_cosf_exact_tests[i].x, z, s_cosf_exact_tests[i].result);
    }
}

#endif // HAS_COSF

#if defined(HAS_COSF) || defined(HAS_LIBM_SSE2)

static TESTENTRY_DBL_APPROX s_cosf_approx_tests[] =
{
//  {    x,                     {    y_rounded,               y_difference           } }
    {   -0x1.eadfb40000000p+14, {     0x1.fffff6720b4d7p-1,   -0x1.caab3625bb93bp-60 }, 1 }, // cosf(-31415.92578125) == 0.99999971525326258726
    {   -0x1.ec19de0000000p+14, {    -0x1.fffffcaedff8ep-1,   -0x1.ccea382f6b4a7p-56 }, 1 }, // cosf(-31494.466796875) == -0.99999990114882722215
    {   -0x1.ed54060000000p+14, {     0x1.fffffe655ff0dp-1,   -0x1.199c29847f8d0p-56 }, 1 }, // cosf(-31573.005859375) == 0.99999995219693148113
    {   -0x1.ee8e300000000p+14, {    -0x1.fffff2b552c64p-1,    0x1.dbf46e5c8d090p-56 }, 1 }, // cosf(-31651.546875) == -0.99999960387630124266
    {   -0x1.efc8580000000p+14, {     0x1.ffffffb046ab1p-1,    0x1.c045a6f6943eap-56 }, 1 }, // cosf(-31730.0859375) == 0.99999999071891046075
    {   -0x1.f102800000000p+14, {    -0x1.fffff999ca191p-1,    0x1.2fab6df536b2cp-55 }, 1 }, // cosf(-31808.625) == -0.99999980928719097755
    {   -0x1.f23caa0000000p+14, {     0x1.fffffa52bf77fp-1,    0x1.a982aa27ffcb8p-56 }, 1 }, // cosf(-31887.166015625) == 0.99999983081919188252
    {   -0x1.f376d20000000p+14, {    -0x1.ffffff80b6dadp-1,    0x1.a5b05c534e3dcp-55 }, 1 }, // cosf(-31965.705078125) == -0.99999998518199146737
    {   -0x1.f4b0fa0000000p+14, {     0x1.fffff19d3dc88p-1,    0x1.5f2fa284e3b34p-55 }, 1 }, // cosf(-32044.244140625) == 0.99999957127046549743
    {    -0x1.3a28c60000000p+8, {     0x1.ffffffffd9f80p-1,    0x1.11ef5ad6631c5p-55 }, 1 }, // cosf(-314.1592712402344) == 0.99999999998270541951
    {    -0x1.395fb60000000p+8, {     0x1.6a0a3d1a7a6dap-1,   -0x1.977e303298c2dp-57 }, 1 }, // cosf(-313.3738708496094) == 0.7071093649694504158
    {    -0x1.3896a60000000p+8, {    0x1.7f00f2211ed25p-20,    0x1.d77853d637f6bp-74 }, 1 }, // cosf(-312.5884704589844) == 1.4267999477724828801e-6
    {    -0x1.37cd960000000p+8, {    -0x1.6a09f965bf8b6p-1,    0x1.f750c92d6486dp-56 }, 1 }, // cosf(-311.8030700683594) == -0.70710734717410758712
    {    -0x1.3704860000000p+8, {    -0x1.fffffffff5ebdp-1,    0x1.052b9f4a06434p-56 }, 1 }, // cosf(-311.0176696777344) == -0.99999999999541665213
    {    -0x1.363b760000000p+8, {    -0x1.6a0969b9b4964p-1,   -0x1.435b1aa2ab474p-58 }, 1 }, // cosf(-310.2322692871094) == -0.70710306541358792115
    {    -0x1.3572660000000p+8, {    0x1.f61daceb80bc5p-18,   -0x1.ad000756a30dcp-72 }, 1 }, // cosf(-309.4468688964844) == 7.482110258918759823e-6
    {    -0x1.34a9560000000p+8, {     0x1.6a0accc631809p-1,   -0x1.8b6db2e4ccac8p-55 }, 1 }, // cosf(-308.6614685058594) == 0.70711364669182426177
    {    -0x1.33e0460000000p+8, {     0x1.ffffffff63570p-1,   -0x1.85999abcbe954p-58 }, 1 }, // cosf(-307.8760681152344) == 0.99999999992875920368
    {    -0x1.921fb60000000p+1, {    -0x1.fffffffffffdep-1,    0x1.adedb638bdc45p-55 }, 1 }, // cosf(-3.1415927410125732) == -0.99999999999999617863
    {    -0x1.83c3260000000p+1, {    -0x1.fcc7d8ee629eap-1,   -0x1.c092403527851p-57 }, 1 }, // cosf(-3.029392957687378) == -0.99371221456504811415
    {    -0x1.7566960000000p+1, {    -0x1.f329c04df75e1p-1,   -0x1.94b5906792936p-56 }, 1 }, // cosf(-2.9171931743621826) == -0.97492791129814917435
    {    -0x1.670a060000000p+1, {    -0x1.e344ac78ea652p-1,    0x1.9f61256bfdbadp-55 }, 1 }, // cosf(-2.8049933910369873) == -0.94388331390400477198
    {    -0x1.58ad760000000p+1, {    -0x1.cd4bc93947e86p-1,   -0x1.9c6b1fc3ab365p-55 }, 1 }, // cosf(-2.692793607711792) == -0.90096882652501690844
    {    -0x1.4a50e80000000p+1, {    -0x1.b185d74dc3dc9p-1,    0x1.48eb1bb8a423ap-55 }, 1 }, // cosf(-2.580594062805176) == -0.84672425102501688063
    {    -0x1.3bf4580000000p+1, {    -0x1.904c38650a82ap-1,    0x1.3ad47f358d144p-55 }, 1 }, // cosf(-2.4683942794799805) == -0.78183151467715569144
    {    -0x1.2d97c80000000p+1, {    -0x1.6a09e68c2affep-1,   -0x1.6a960142b32c1p-55 }, 1 }, // cosf(-2.356194496154785) == -0.70710678540262942905
    {    -0x1.1f3b380000000p+1, {    -0x1.3f3a0d1de49edp-1,    0x1.cd879482eb8dep-55 }, 1 }, // cosf(-2.24399471282959) == -0.62348977079301232665
    {    -0x1.10dea80000000p+1, {    -0x1.10667fb4be354p-1,    0x1.9504316540b01p-55 }, 1 }, // cosf(-2.1317949295043945) == -0.53203200417838520991
    {    -0x1.02821a0000000p+1, {    -0x1.bc4c0b53cbe22p-2,   -0x1.1a73664d77efap-56 }, 1 }, // cosf(-2.0195953845977783) == -0.43388383578255419891
    {    -0x1.e84b120000000p+0, {    -0x1.5234a9004fb03p-2,   -0x1.3c674cbf335a2p-58 }, 1 }, // cosf(-1.9073954820632935) == -0.33027900757217327084
    {    -0x1.cb91f40000000p+0, {    -0x1.c7b91044a20dfp-3,   -0x1.d100a1778b4a6p-57 }, 1 }, // cosf(-1.7951958179473877) == -0.22252094945384380415
    {    -0x1.aed8d40000000p+0, {    -0x1.ca9b3b3fd03e8p-4,    0x1.49dc6dc52da1fp-60 }, 1 }, // cosf(-1.6829960346221924) == -0.11196444648977854518
    {    -0x1.921fb60000000p+0, {   -0x1.777a5cf72ceccp-25,   -0x1.3378e8c79f568p-79 }, 1 }, // cosf(-1.5707963705062866) == -4.3711390001862414389e-8
    {    -0x1.7566960000000p+0, {     0x1.ca9b43ba6e56cp-4,    0x1.66912e6c0d216p-58 }, 1 }, // cosf(-1.4585965871810913) == 0.11196447807642124148
    {    -0x1.58ad760000000p+0, {     0x1.c7b9146d6d108p-3,    0x1.27fb296e1c85dp-58 }, 1 }, // cosf(-1.346396803855896) == 0.22252098044339896756
    {    -0x1.3bf4580000000p+0, {     0x1.5234ab03c1357p-2,   -0x1.e21cc7cfd8619p-57 }, 1 }, // cosf(-1.2341971397399902) == 0.33027903757493019902
    {    -0x1.1f3b380000000p+0, {     0x1.bc4c060a9ede0p-2,   -0x1.f9879c50ea968p-58 }, 1 }, // cosf(-1.121997356414795) == 0.4338837570173534817
    {    -0x1.02821a0000000p+0, {     0x1.1066809bef9dfp-1,    0x1.cd3047e3bf6cep-55 }, 1 }, // cosf(-1.0097976922988892) == 0.5320320310927932284
    {    -0x1.cb91f40000000p-1, {     0x1.3f3a0df35e12fp-1,    0x1.a916328089ddbp-55 }, 1 }, // cosf(-0.8975979089736938) == 0.62348979564470668081
    {    -0x1.921fb60000000p-1, {     0x1.6a09e5e333598p-1,    0x1.f2d6497399f20p-56 }, 1 }, // cosf(-0.7853981852531433) == 0.70710676573223721282
    {    -0x1.58ad760000000p-1, {     0x1.904c37d00df0ep-1,    0x1.285377f17f363p-55 }, 1 }, // cosf(-0.673198401927948) == 0.78183149733283289089
    {    -0x1.1f3b380000000p-1, {     0x1.b185d5be3b826p-1,    0x1.4d174c237772ep-57 }, 1 }, // cosf(-0.5609986782073975) == 0.84672420451329767147
    {    -0x1.cb91f40000000p-2, {     0x1.cd4bca8de615ap-1,    0x1.a7c4ca68ec1ecp-57 }, 1 }, // cosf(-0.4487989544868469) == 0.90096886617815675796
    {    -0x1.58ad760000000p-2, {     0x1.e344ad27a5d52p-1,   -0x1.023380258c48ep-56 }, 1 }, // cosf(-0.336599200963974) == 0.94388333424550750749
    {    -0x1.cb91f40000000p-3, {     0x1.f329c051c2499p-1,   -0x1.f9e29378a8f8dp-55 }, 1 }, // cosf(-0.22439947724342346) == 0.97492791173967235996
    {    -0x1.cb91f40000000p-4, {     0x1.fcc7d8c54c98fp-1,   -0x1.f2da30acb6667p-56 }, 1 }, // cosf(-0.11219973862171173) == 0.99371220978200533529
    {                 0x0.0p+0, {     0x1.0000000000000p+0,                 0x0.0p+0 }, 1 }, // cosf(0.0) == 1.0
    {     0x1.cb91f40000000p-4, {     0x1.fcc7d8c54c98fp-1,   -0x1.f2da30acb6667p-56 }, 1 }, // cosf(0.11219973862171173) == 0.99371220978200533529
    {     0x1.cb91f40000000p-3, {     0x1.f329c051c2499p-1,   -0x1.f9e29378a8f8dp-55 }, 1 }, // cosf(0.22439947724342346) == 0.97492791173967235996
    {     0x1.58ad760000000p-2, {     0x1.e344ad27a5d52p-1,   -0x1.023380258c48ep-56 }, 1 }, // cosf(0.336599200963974) == 0.94388333424550750749
    {     0x1.cb91f40000000p-2, {     0x1.cd4bca8de615ap-1,    0x1.a7c4ca68ec1ecp-57 }, 1 }, // cosf(0.4487989544868469) == 0.90096886617815675796
    {     0x1.1f3b380000000p-1, {     0x1.b185d5be3b826p-1,    0x1.4d174c237772ep-57 }, 1 }, // cosf(0.5609986782073975) == 0.84672420451329767147
    {     0x1.58ad760000000p-1, {     0x1.904c37d00df0ep-1,    0x1.285377f17f363p-55 }, 1 }, // cosf(0.673198401927948) == 0.78183149733283289089
    {     0x1.921fb60000000p-1, {     0x1.6a09e5e333598p-1,    0x1.f2d6497399f20p-56 }, 1 }, // cosf(0.7853981852531433) == 0.70710676573223721282
    {     0x1.cb91f40000000p-1, {     0x1.3f3a0df35e12fp-1,    0x1.a916328089ddbp-55 }, 1 }, // cosf(0.8975979089736938) == 0.62348979564470668081
    {     0x1.02821a0000000p+0, {     0x1.1066809bef9dfp-1,    0x1.cd3047e3bf6cep-55 }, 1 }, // cosf(1.0097976922988892) == 0.5320320310927932284
    {     0x1.1f3b380000000p+0, {     0x1.bc4c060a9ede0p-2,   -0x1.f9879c50ea968p-58 }, 1 }, // cosf(1.121997356414795) == 0.4338837570173534817
    {     0x1.3bf4580000000p+0, {     0x1.5234ab03c1357p-2,   -0x1.e21cc7cfd8619p-57 }, 1 }, // cosf(1.2341971397399902) == 0.33027903757493019902
    {     0x1.58ad760000000p+0, {     0x1.c7b9146d6d108p-3,    0x1.27fb296e1c85dp-58 }, 1 }, // cosf(1.346396803855896) == 0.22252098044339896756
    {     0x1.7566960000000p+0, {     0x1.ca9b43ba6e56cp-4,    0x1.66912e6c0d216p-58 }, 1 }, // cosf(1.4585965871810913) == 0.11196447807642124148
    {     0x1.921fb60000000p+0, {   -0x1.777a5cf72ceccp-25,   -0x1.3378e8c79f568p-79 }, 1 }, // cosf(1.5707963705062866) == -4.3711390001862414389e-8
    {     0x1.aed8d40000000p+0, {    -0x1.ca9b3b3fd03e8p-4,    0x1.49dc6dc52da1fp-60 }, 1 }, // cosf(1.6829960346221924) == -0.11196444648977854518
    {     0x1.cb91f40000000p+0, {    -0x1.c7b91044a20dfp-3,   -0x1.d100a1778b4a6p-57 }, 1 }, // cosf(1.7951958179473877) == -0.22252094945384380415
    {     0x1.e84b120000000p+0, {    -0x1.5234a9004fb03p-2,   -0x1.3c674cbf335a2p-58 }, 1 }, // cosf(1.9073954820632935) == -0.33027900757217327084
    {     0x1.02821a0000000p+1, {    -0x1.bc4c0b53cbe22p-2,   -0x1.1a73664d77efap-56 }, 1 }, // cosf(2.0195953845977783) == -0.43388383578255419891
    {     0x1.10dea80000000p+1, {    -0x1.10667fb4be354p-1,    0x1.9504316540b01p-55 }, 1 }, // cosf(2.1317949295043945) == -0.53203200417838520991
    {     0x1.1f3b380000000p+1, {    -0x1.3f3a0d1de49edp-1,    0x1.cd879482eb8dep-55 }, 1 }, // cosf(2.24399471282959) == -0.62348977079301232665
    {     0x1.2d97c80000000p+1, {    -0x1.6a09e68c2affep-1,   -0x1.6a960142b32c1p-55 }, 1 }, // cosf(2.356194496154785) == -0.70710678540262942905
    {     0x1.3bf4580000000p+1, {    -0x1.904c38650a82ap-1,    0x1.3ad47f358d144p-55 }, 1 }, // cosf(2.4683942794799805) == -0.78183151467715569144
    {     0x1.4a50e80000000p+1, {    -0x1.b185d74dc3dc9p-1,    0x1.48eb1bb8a423ap-55 }, 1 }, // cosf(2.580594062805176) == -0.84672425102501688063
    {     0x1.58ad760000000p+1, {    -0x1.cd4bc93947e86p-1,   -0x1.9c6b1fc3ab365p-55 }, 1 }, // cosf(2.692793607711792) == -0.90096882652501690844
    {     0x1.670a060000000p+1, {    -0x1.e344ac78ea652p-1,    0x1.9f61256bfdbadp-55 }, 1 }, // cosf(2.8049933910369873) == -0.94388331390400477198
    {     0x1.7566960000000p+1, {    -0x1.f329c04df75e1p-1,   -0x1.94b5906792936p-56 }, 1 }, // cosf(2.9171931743621826) == -0.97492791129814917435
    {     0x1.83c3260000000p+1, {    -0x1.fcc7d8ee629eap-1,   -0x1.c092403527851p-57 }, 1 }, // cosf(3.029392957687378) == -0.99371221456504811415
    {     0x1.921fb60000000p+1, {    -0x1.fffffffffffdep-1,    0x1.adedb638bdc45p-55 }, 1 }, // cosf(3.1415927410125732) == -0.99999999999999617863
    {    0x1.88b2f80000000p+12, {     0x1.ffffff093cc76p-1,   -0x1.8ec79178f5aa5p-58 }, 1 }, // cosf(6283.185546875) == 0.99999997127305450554
    {    0x1.88bf880000000p+12, {     0x1.6a0a1b07f946fp-1,    0x1.4db8ae075b759p-57 }, 1 }, // cosf(6283.970703125) == 0.70710834953628233029
    {    0x1.88cc180000000p+12, {    0x1.fffb093b7bd3cp-13,   -0x1.df8ec7418e253p-67 }, 1 }, // cosf(6284.755859375) == 0.00024413137894850079169
    {    0x1.88d8aa0000000p+12, {    -0x1.6a0a1b7843dc6p-1,    0x1.7f43dde4e4f20p-57 }, 1 }, // cosf(6285.54150390625) == -0.70710836260871511377
    {    0x1.88e53a0000000p+12, {    -0x1.ffffff094685ep-1,   -0x1.9f659d91c4c18p-57 }, 1 }, // cosf(6286.32666015625) == -0.99999997127748564434
    {    0x1.88f1cc0000000p+12, {    -0x1.6a0947a71f8aep-1,   -0x1.67c2813b39e00p-55 }, 1 }, // cosf(6287.1123046875) == -0.7071020499713734199
    {    0x1.88fe5c0000000p+12, {   -0x1.ed4c19f8de03bp-13,    0x1.1ae4f556325e3p-68 }, 1 }, // cosf(6287.8974609375) == -0.00023522246899765501766
    {    0x1.890aee0000000p+12, {     0x1.6a0aeed8a1ecdp-1,    0x1.2562159fc100ap-55 }, 1 }, // cosf(6288.68310546875) == 0.70711466211738530491
    {    0x1.89177e0000000p+12, {     0x1.ffffff1b46be3p-1,    0x1.8518bb79e254fp-55 }, 1 }, // cosf(6289.46826171875) == 0.99999997337306149925
    {    0x1.7f7ec60000000p+22, {     0x1.f682ee3035e5ep-1,   -0x1.361e6347ab407p-56 }, 1 }, // cosf(6283185.5) == 0.98146766985423615199
    {    0x1.812c480000000p+22, {     0x1.f0f85ecd81a70p-1,    0x1.fab7a971127b2p-55 }, 1 }, // cosf(6310674.0) == 0.97064491518615453268
    {    0x1.82d9cc0000000p+22, {     0x1.f7daae4a37eefp-1,   -0x1.77d3b300e3c92p-59 }, 1 }, // cosf(6338163.0) == 0.98409027725207731419
    {    0x1.8487500000000p+22, {     0x1.fca831be87222p-1,    0x1.68cbf6e7287c4p-55 }, 1 }, // cosf(6365652.0) == 0.99347072077540969385
    {    0x1.8634d40000000p+22, {     0x1.ff5bd5068e3f6p-1,    0x1.2575006ebddf9p-60 }, 1 }, // cosf(6393141.0) == 0.9987474985601909615
    {    0x1.87e2580000000p+22, {     0x1.fff2bcc43ec8bp-1,   -0x1.0dca912c18094p-55 }, 1 }, // cosf(6420630.0) == 0.99989881415912526392
    {    0x1.898fdc0000000p+22, {     0x1.fe6c4964bdd53p-1,   -0x1.e560127c2f5c8p-56 }, 1 }, // cosf(6448119.0) == 0.99691991190676740453
    {    0x1.8b3d600000000p+22, {     0x1.faca17c921821p-1,    0x1.88027c587ac0dp-55 }, 1 }, // cosf(6475608.0) == 0.9898230965634448467
    {    0x1.8ceae40000000p+22, {     0x1.f50fff91d8541p-1,    0x1.a57a776de50afp-59 }, 1 }, // cosf(6503097.0) == 0.97863768248877403285
};

#endif // defined(HAS_COSF) || defined(HAS_LIBM_SSE2)

#if defined(HAS_COSF)

void Test_cosf_approx(void)
{
    for (int i = 0; i < _countof(s_cosf_approx_tests); i++)
    {
        float x = s_cosf_approx_tests[i].x;
        float expected = s_cosf_approx_tests[i].expected.rounded;
        float z = cosf(x);
        int64_t error = abs(ulp_error_flt(expected, z));
        ok(error <= s_cos_approx_tests[i].max_error,
            "cos(%.6e) = %.7e, expected %.7e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_cos_approx_tests[i].max_error);
    }
}

#endif // defined(HAS_COSF)

#if defined(HAS_LIBM_SSE2)

__ATTRIBUTE_SSE2__ __m128d __libm_sse2_cos(__m128d Xmm0);

__ATTRIBUTE_SSE2__
void Test_libm_sse2_cos(void)
{
    int i;
    for (i = 0; i < _countof(s_cos_approx_tests); i++)
    {
        double x = s_cos_approx_tests[i].x;
        double expected = s_cos_approx_tests[i].expected.rounded;
        __m128d xmm0 = _mm_set_sd(x);
        __m128d xmm1 = __libm_sse2_cos(xmm0);
        double z = _mm_cvtsd_f64(xmm1);
        int64_t error = ulp_error_precise(&s_cos_approx_tests[i].expected, z);
        ok(error <= s_cos_approx_tests[i].max_error,
            "__libm_sse2_cos(%.17e) = %.17e, expected %.17e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_cos_approx_tests[i].max_error);
    }
}

__ATTRIBUTE_SSE2__ __m128 __libm_sse2_cosf(__m128 Xmm0);

__ATTRIBUTE_SSE2__
void Test_libm_sse2_cosf(void)
{
    int i;
    for (i = 0; i < _countof(s_cosf_approx_tests); i++)
    {
        float x = s_cosf_approx_tests[i].x;
        float expected = s_cosf_approx_tests[i].expected.rounded;
        __m128 xmm0 = _mm_set_ps1(x);
        __m128 xmm1 = __libm_sse2_cosf(xmm0);
        float z = _mm_cvtss_f32(xmm1);
        int64_t error = ulp_error_flt(expected, z);
        ok(error <= s_cosf_approx_tests[i].max_error,
            "__libm_sse2_cosf(%.6e) = %.7e, expected %.7e, error %I64d ULPs, max %u ULPs\n",
            x, z, expected, error, s_cosf_approx_tests[i].max_error);
    }
}

#endif // defined(HAS_LIBM_SSE2)

START_TEST(cos)
{
    Test_cos_exact();
    Test_cos_approx();
#if defined(HAS_COSF)
    Test_cosf_exact();
    Test_cosf_approx();
#endif
#if defined(HAS_LIBM_SSE2)
    Test_libm_sse2_cos();
    Test_libm_sse2_cosf();
#endif
}
